﻿using System;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Net.Sockets;
using System.Xml;
using System.Xml.Serialization;
using ChatClientApp.Tools;
using ChatServerApp.Classes;

namespace ChatClientApp.Classes
{
    class ServerHandling
    {
        IPAddress serverIp;
        int serverPort;
        TcpClient tcpclient;
        private static string ACK = "ACK";
        private static string NEG = "NEG";

        public ServerHandling(IPEndPoint ipPort)
        {
            this.serverIp = ipPort.Address;
            this.serverPort = ipPort.Port;
            tcpclient = new TcpClient();
        }

        public IPEndPoint RequestClientListening(string clientName)
        {
            try
            {
                string hostname = Tools.Tools.HOSTNAME;
                IPAddress[] adresslist = Dns.GetHostAddresses(hostname);
                foreach (IPAddress ipa in adresslist)
                {
                    if (ipa.AddressFamily == AddressFamily.InterNetwork)
                    {
                        tcpclient = new TcpClient();
                        tcpclient.Connect(ipa, 1500);
                        break;
                    }
                }
                StreamReader reader = new StreamReader(tcpclient.GetStream());
                StreamWriter writer = new StreamWriter(tcpclient.GetStream());
                string temp = reader.ReadLine();
                string ip = "";
                string port = "";
                string status = "";

                writer.WriteLine("GET IP");
                writer.Flush();
                temp = reader.ReadLine();
                if (temp == "ACK")
                {
                    writer.WriteLine(clientName);
                    writer.Flush();
                    ip = reader.ReadLine();
                    port = reader.ReadLine();
                    status = reader.ReadLine();
                }

                if (!(Convert.ToBoolean(status)))
                    return null;

                return new IPEndPoint(IPAddress.Parse(ip), Convert.ToInt32(port));

            }
            catch (Exception e)
            {

            }
            return null;
        }

        public string[,] contactsOnline(string[] contactList)
        {
            string[,] ret = new string[contactList.Length, 2];
            string temp = String.Empty;
            int counter = 0;

            try
            {
                tcpclient = new TcpClient();
                tcpclient.Connect(this.serverIp, this.serverPort);

                StreamReader reader = new StreamReader(tcpclient.GetStream());
                StreamWriter writer = new StreamWriter(tcpclient.GetStream());

                temp = reader.ReadLine();

                writer.WriteLine("IS ONLINE");
                writer.Flush();

                temp = reader.ReadLine();

                if (temp == "ACK")
                {
                    foreach (string element in contactList)
                    {
                        writer.WriteLine(element);
                        writer.Flush();
                        ret[counter, 0] = element;
                        ret[counter, 1] = reader.ReadLine();
                        counter++;
                    }

                    writer.WriteLine("FINISHED");
                    writer.Flush();
                }
                return ret;

            }
            catch (Exception e)
            {
                counter = 0;
                foreach (string element in contactList)
                {
                    ret[counter, 0] = element;
                    ret[counter, 1] = "false";
                    counter++;
                }
            }
            return ret;
        }

        // REGISTER
        /// <summary>
        /// REGISTER
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static string Registration(User_Complete user)
        {
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("REGISTER");
                srv_Message = sc.read();
                if (srv_Message.Equals(ACK))
                {
                    sc.write(user.Name);
                    sc.write(user.Vorname);
                    sc.write(user.Nick);
                    sc.write(user.Pw);
                    sc.write(user.Jahrgang);
                    sc.write(user.Email);
                    srv_Message = sc.read();
                    if (srv_Message.Equals(ACK))
                        return ACK;
                    else if (srv_Message.Equals(NEG))
                        return sc.read();
                }
            }
            catch (Exception e)
            {
                return "Server probably down";
            }
            return "Unknown problem...";
        }

        // LOGIN
        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public static string Login(string username, string password)
        {
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("LOGIN");
                srv_Message = sc.read();
                if (srv_Message.Equals(ACK))
                {
                    sc.write(username);
                    sc.write(password);
                    sc.write(ActiveComponents.ActiveServerPort.ToString());

                    srv_Message = sc.read();
                    if (srv_Message.Equals(ACK))
                    {
                        string ret = sc.read();
                        
                        srv_Message = sc.readToEnd();

                        ActiveComponents.ActiveUser = DeserializeXmlString<User_Complete>(srv_Message);

                        return ret;
                    }
                    else if (srv_Message.Equals(NEG))
                    {
                        srv_Message = sc.read();
                        return srv_Message;
                    }
                    
                }
                else
                {
                    return "Login not possible.";
                }
            }
            catch (Exception e)
            {
                return "Login not possible. " + e.Message;
            }
            return String.Empty;
        }

        // LOGOUT
        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public static bool Logout(string username)
        {
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("LOGOUT");
                srv_Message = sc.read();
                if (srv_Message.Equals(ACK))
                {
                    sc.write(username);
                    srv_Message = sc.read();
                    if (srv_Message.Equals(ACK))
                        return true;
                }
            }
            catch
            {
                return false;
            }
            return false;
        }

        // REQUEST IP
        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public static IPEndPoint RequestUserIP(string username)
        {
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                string ip = "";
                string port = "";
                string status = "";

                srv_Message = sc.read();
                sc.write("GET IP");
                srv_Message = sc.read();
                if (srv_Message.Equals(ACK))
                {
                    sc.write(username);
                    ip = sc.read();
                    port = sc.read();
                    status = sc.read();

                    if (!(Convert.ToBoolean(status)))
                        return null;

                    return new IPEndPoint(IPAddress.Parse(ip), Convert.ToInt32(port));
                }
            }
            catch
            {
            }
            return null;

        }

        // IS ONLINE
        /// <summary>
        /// 
        /// </summary>
        /// <param name="requestUsers"></param>
        /// <returns></returns>
        public static string[,] ContactsOnline(List<string> requestUsers)
        {
            string[,] ret = new string[requestUsers.Count, 2];
            string[] tempArray = new string[requestUsers.Count];
            requestUsers.CopyTo(tempArray);
            string temp = String.Empty;
            int counter = 0;

            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("IS ONLINE");
                srv_Message = sc.read();
                if (srv_Message.Equals(ACK))
                {
                    foreach (string element in tempArray)
                    {
                        sc.write(element);
                        string tempOnline = sc.read();
                        ret[counter,0] = element;
                        ret[counter,1] = tempOnline;
                        counter++;
                    }

                    sc.write("FINISHED");
                    srv_Message = sc.read();

                    if (!srv_Message.Equals(ACK))
                        throw new Exception();
                }
            }
            catch
            {
                for (int i = 0; i < requestUsers.Count; i++)
                {
                    ret[i, 0] = tempArray[i];
                    ret[i, 1] = "False";
                }
            }
            return ret;

        }

        // SEARCH USER
        /// <summary>
        /// 
        /// </summary>
        /// <param name="p"></param>
        internal static List<User> SearchUser(string requestUser)
        {
            List<User> ret = new List<User>();

            string temp = String.Empty;
            int counter = 0;

            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("SEARCH USER");
                srv_Message = sc.read();

                if (srv_Message.Equals(ACK))
                {
                    sc.write(requestUser);
                    string count = sc.read();
                    try
                    {
                        string tempOnline = String.Empty;

                        if (Convert.ToInt32(count) > 0)
                            tempOnline = sc.readToEnd();
                        else 
                            tempOnline = sc.read();

                        if (tempOnline.Contains("NO USER FOUND"))
                            return ret;
                        else
                        {
                            ret.Add( DeserializeXmlString<User>(tempOnline));
                        }
                    }
                    catch
                    {

                    }
                }
            }
            catch
            {
            }
            return ret;

        }

        // ADD CONTACT
        /// <summary>
        /// 
        /// </summary>
        /// <param name="currentUserNick"></param>
        /// <param name="listOfUsers"></param>
        internal static void AddContacts(string currentUserNick, List<string> listOfUsers)
        {
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("ADD CONTACT");
                srv_Message = sc.read();

                if (srv_Message.Equals(ACK))
                {
                    string[] arrayOfUsers = listOfUsers.ToArray();
                    foreach (string element in arrayOfUsers)
                    {
                        sc.write(currentUserNick + "|" + element);
                    }
                    sc.write("FINISHED");
                    srv_Message = sc.read();
                    if (srv_Message.Equals(ACK))
                        return;
                }
            }
            catch
            {

            }
        }

        // REMOVE CONTACT
        /// <summary>
        /// 
        /// </summary>
        /// <param name="currentUserNick"></param>
        /// <param name="listOfUsers"></param>
        internal static void RemoveContacts(string currentUserNick, List<string> listOfUsers)
        {
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("REMOVE CONTACT");
                srv_Message = sc.read();

                if (srv_Message.Equals(ACK))
                {
                    string[] arrayOfUsers = listOfUsers.ToArray();
                    foreach (string element in arrayOfUsers)
                    {
                        sc.write(currentUserNick + "|" + element);
                    }
                    sc.write("FINISHED");
                    srv_Message = sc.read();
                    if (srv_Message.Equals(ACK))
                        return;
                }
            }
            catch
            {

            }
        }

        // GET CONTACT COUNT
        /// <summary>
        /// 
        /// </summary>
        /// <param name="nick"></param>
        /// <returns></returns>
        internal static int GetContactCount(string nick)
        {
            int ret = 0;
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("GET CONTACT COUNT");
                srv_Message = sc.read();

                if (srv_Message.Equals(ACK))
                {
                    sc.write(nick);
                    srv_Message = sc.read();
                    ret = Convert.ToInt32(srv_Message);
                }
            }
            catch
            {

            }
            return ret;
        }

        // GET CONTACTS
        /// <summary>
        /// 
        /// </summary>
        /// <param name="nick"></param>
        /// <returns></returns>
        internal static List<string> GetContacts(string nick)
        {
            List<string> retList = new List<string>();
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("GET CONTACTS");
                srv_Message = sc.read();

                if (srv_Message.Equals(ACK))
                {
                    sc.write(nick);
                    
                    srv_Message = sc.read();
                    while (srv_Message != "FINISHED")
                    {
                        retList.Add(srv_Message);
                        srv_Message = sc.read();
                    }
                }
            }
            catch
            {

            }
            return retList;
        }

        // IS FREE NICK
        /// <summary>
        /// 
        /// </summary>
        /// <param name="nickname"></param>
        /// <returns></returns>
        internal static bool IsFreeNick(string nickname)
        {
            int ret = -1;
            try
            {
                string srv_Message = String.Empty;
                ServerConnection sc = new ServerConnection(Tools.Tools.SERVER_ENDPOINT);
                srv_Message = sc.read();
                sc.write("IS FREE NICK");
                srv_Message = sc.read();

                if (srv_Message.Equals(ACK))
                {
                    sc.write(nickname);
                    srv_Message = sc.read();
                    ret = Convert.ToInt32(srv_Message);
                }
            }
            catch
            {

            }

            if (ret == 0)
                return true;
            else 
                return false;
        }

        #region Serialization
        private static T DeserializeXmlString<T>(string xmlStringObject)
        {
            XmlSerializer ser = new XmlSerializer(typeof(T));
            StringReader re = new StringReader(xmlStringObject);
            XmlTextReader rea = new XmlTextReader(re);
            // StreamReader sr = new StreamReader(rea);
            return (T)ser.Deserialize(rea);
        }
        #endregion

    }
}
